package se.sics.tac.aw;

import static se.sics.tac.aw.TACAgent.*;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Vector;
import java.util.logging.Logger;

public class TicketAllocator {
	private HashMap<Integer, Integer> ticketsToAllocate;
	static private int[] sortedTicketPrices;
	static private HashMap<Integer, String> clientEntPrefsAsMap;
	private int profit;
	private int lastProfit;
	private TACAgent agent;
	private Logger log;
	private int[][] allocatedTickets;
	private HashMap<Integer, Integer> breakEvenPrice;
	private HashMap<Integer, Integer> lowestSalePrice;
	ArrayList[] bidPrices = new ArrayList[12]; // price to bid, [auctionID-16]

	TicketAllocator(TACAgent agent, Logger log) {
		this.ticketsToAllocate = new HashMap<Integer, Integer>();
		this.lowestSalePrice = new HashMap<Integer, Integer>();
		this.breakEvenPrice = new HashMap<Integer, Integer>();
		this.agent = agent;
		this.log = log;
	}

	public int getProfit() {
		return profit;
	}

	/**
	 * Sets an map of the tickets that we own, key=auction, value=tickets owned
	 */
	public void resetTicketsToAllocate() {
		int value;
		for (int auction = 16; auction < 28; auction++) {
			value = agent.getOwn(auction);
			// log.finer("ZZsTA " + value +" " + auction);
			ticketsToAllocate.put(auction, value);
		}
	}

	public HashMap<Integer, Integer> getTicketsToAllocate() {
		return ticketsToAllocate;
	}

	public int getTicketsToAllocate(int auction) {
		return ticketsToAllocate.get(auction);
	}

	@SuppressWarnings("unchecked")
	public float[] getBidPrices(int auction) {
		int size = bidPrices[auction - 16].size();
		ArrayList<Integer> intArrayList = bidPrices[auction - 16];
		float[] intArray = new float[size];
		
		if (size > 0) {
			if ( intArray[0] > 0) {
				for (int j = 0; j < size; j++)
					intArray[j] = intArrayList.get(j);
			}else {
				for (int j = 0; j < size; j++)
					intArray[j] = intArrayList.get(size -j -1);
			}
		}
		
		return intArray;
	}

	/**
	 * Gets prices that clients will pay and orders them smallest to highest in
	 * sortedTickePrices
	 */
	public void setSortedTicketPrices() {
		clientEntPrefsAsMap = new HashMap<Integer, String>();
		ArrayList<Integer> a = new ArrayList<Integer>();
		int key;
		for (int client = 0; client < 8; client++) {
			key = agent.getClientPreference(client, TACAgent.E1);
			addToMap(key, 1, client);
			if (!a.contains(key)) {
				a.add(key);
			}

			key = agent.getClientPreference(client, TACAgent.E2);
			addToMap(key, 2, client);
			if (!a.contains(key)) {
				a.add(key);
			}

			key = agent.getClientPreference(client, TACAgent.E3);
			addToMap(key, 3, client);
			if (!a.contains(key)) {
				a.add(key);
			}

		}

		int k = a.size();
		int[] b = new int[k];

		Iterator<Integer> ai = a.iterator();
		int i = 0;
		while (ai.hasNext()) {
			b[i] = ai.next();
			i++;
		}
		sortedTicketPrices = InsertionSort.insertionSort(b, k);
	}

	/**
	 * Constructs a map keyed by ticket cost, the values are strings of
	 * "client:entertainment type".
	 */
	public void addToMap(int key, int etype, int client) {
		String value;
		if (clientEntPrefsAsMap.containsKey(key)) {
			value = clientEntPrefsAsMap.get(key) + ":" + Integer.toString(client) + ":" + etype;
			clientEntPrefsAsMap.put(key, value);
		} else {
			value = Integer.toString(client) + ":" + etype;
			clientEntPrefsAsMap.put(key, value);
		}
	}

	public int[] getSortedTicketPrices() {
		return sortedTicketPrices;
	}

	public HashMap<Integer, String> getClientEntPrefsAsMap() {
		return clientEntPrefsAsMap;
	}

	/**
	 * Returns that auction Id of of the ent allocated to the client. Client
	 * expected in range 0-7, ent is 1, 2, 3.
	 */
	public int getAuctionID(int client, int ent) {

		for (int i = 0; i < 5; i++) {
			int auction = allocatedTickets[client][i];
			if (auction > 0) {
				if (TACAgent.getAuctionType(auction) == ent) {
					return auction;
				}
			}
		}
		return -1;
	}

	/**
	 * Finds the optimum distribution of tickets amongst clients by ensuring
	 * that the highest paying clients get the tickets that they want. At the
	 * end the map ticketsToAllocate will have negative values if we need to buy
	 * tickets, positive if we need to sell, and zero if we have the right
	 * number.
	 * 
	 * @param inFlights
	 * @param outFlights
	 */
	public void findBestTicketDistribution(int[] inFlights, int[] outFlights) {
		
		allocatedTickets = new int[9][5]; // auctionID assigned to
		// [client][day]

		for (int i = 0; i < 12; i++)
			bidPrices[i] = new ArrayList<Integer>();

		resetTicketsToAllocate();

		// For each client premium in descending order
		for (int priceIndex = sortedTicketPrices.length - 1; priceIndex > -1; priceIndex--) {

			// For each ent assigned to a client with the same price. ex price
			// -> cli:ent:cli:ent....
			String[] clientEntMap = clientEntPrefsAsMap.get(sortedTicketPrices[priceIndex]).split(
					":");

			for (int clientEntMapIndex = 0; clientEntMapIndex < clientEntMap.length; clientEntMapIndex += 2) {
				int client = Integer.parseInt(clientEntMap[clientEntMapIndex]);
				int entNo = Integer.parseInt(clientEntMap[clientEntMapIndex + 1]);
				boolean ticketAllocated = false;

				int firstDay = inFlights[client];
				int lastDay = outFlights[client];
				if (firstDay < 0)
					break;

				// allocate first available ticket on first available day
				for (int day = firstDay; day < lastDay; day++) {

					int auction = getAuctionFor(CAT_ENTERTAINMENT, entNo, day);
					if (ticketsToAllocate.get(auction) > 0 && allocatedTickets[client][day] == 0) {

						allocatedTickets[client][day] = auction;
						ticketsToAllocate.put(auction, ticketsToAllocate.get(auction) - 1);

						if (sortedTicketPrices[priceIndex] < 100) {
							bidPrices[auction - 16].add(-sortedTicketPrices[priceIndex]);
						}

						ticketAllocated = true;
						break;
					}
				}

				// Get the value of a particular ticket
				int ticketPremium = 0;
				ticketPremium = agent.getClientPreference(client, entNo + 2);

				// if a ticket was not allocated and ticket premium id > $100
				// try to buy one
				if (!ticketAllocated && ticketPremium > 100) {

					for (int day = firstDay; day < lastDay; day++) {
						int auction = getAuctionFor(CAT_ENTERTAINMENT, entNo, day);

						if (ticketsToAllocate.get(auction) <= 0
								&& allocatedTickets[client][day] == 0) {

							ticketsToAllocate.put(auction, ticketsToAllocate.get(auction) - 1);
							bidPrices[auction - 16].add(sortedTicketPrices[priceIndex]);
						}
					}
				}
			}
		}

		// add bit prices for unallocated tickets
		for (int auction = MIN_ENTERTAINMENT; auction <= MAX_ENTERTAINMENT; auction++) {
			for (int i = 0; i < ticketsToAllocate.get(auction); i++)
				bidPrices[auction - 16].add(-1);

		}
	}
}